#include "FilePath.h"
#include "Assertions.h"
#include "MemoryManager.h"
#include <cstring>

namespace CEngine
{

#ifdef WINDOWS

#include <windows.h>
#include <Shlwapi.h>

FilePath::FilePath(void)
    :   path(NULL),
        length(0)
{}

FilePath::FilePath(CHAR* p, bool process)
    :   path(NULL),
        length(0)
{
    ASSERT(p != NULL);

    for(U32 i = 0; p[i] != '\0'; ++i) {
        ++length;
    }

    path = (CHAR*)MemoryManager::rawAlloc(length + 1);

    for (U32 i = 0; i <= length; ++i) {
        path[i] = p[i];
    }

    if(process) {
        length = processPath(path);
    }
}

FilePath::FilePath(const FilePath& fp)
    :   path(NULL),
        length(fp.length)
{
    if(fp.getFullPath() != NULL) {
        path = (CHAR*)MemoryManager::rawAlloc(length + 1);
        for (U32 i = 0; i <= length; ++i) {
            path[i] = fp.path[i];
        }
    }
}

FilePath&
FilePath::operator=(const FilePath& fp) {
    if(this == &fp) {
        return (*this);
    }

    length = fp.length;
    if(fp.path == NULL) {
        if(path) {
            MemoryManager::free(path);
        }
        return *this;
    }

    if(path) {
        MemoryManager::free(path);
    }
    path = (CHAR*)MemoryManager::rawAlloc(length + 1);

    for (U32 i = 0; i <= length; ++i) {
        path[i] = fp.path[i];
    }

    return *this;
}

FilePath&
FilePath::operator=(CHAR* p) {
    length = 0;
    if(p == NULL) {
        if(path) {
            MemoryManager::free(path);
        }
        return *this;
    }

    for(U32 i = 0; p[i] != '\0'; ++i) {
        ++length;
    }

    if(path) {
        MemoryManager::free(path);
    }
    path = (CHAR*)MemoryManager::rawAlloc(length + 1);

    for (U32 i = 0; i <= length; ++i) {
        path[i] = p[i];
    }

    return *this;
}

FilePath::~FilePath(void) {
    if(path) {
        MemoryManager::free(path);
    }
    path = NULL;
    length = 0;
}

bool
FilePath::operator== (const FilePath& p) {
    if(path == p.path) {
        return true;
    }
    if(path == NULL || p.path == NULL) {
        return false;
    }

    if(!strcmp(path, p.path)) {
        return true;
    }
    return false;
}

bool
FilePath::operator== (CHAR* p) {
    if(path == p) {
        return true;
    }
    if(path == NULL || p == NULL) {
        return false;
    }

    if(!strcmp(path, p)) {
        return true;
    }
    return false;
}

U32
FilePath::processPath(CHAR* p) {
    U32 length = 0;
    bool lastCharacterWasSeparator = false;

    for (U32 i = 0; p[i] != '\0'; ++i) {
        if(p[i] == '/' || (i != 1 && p[i] == ':')) {
            p[i] = '\\';
        }
    }

    for (U32 i = 0; p[i] != '\0'; ++i) {
        if(p[i] == '\\') {
            while(lastCharacterWasSeparator && p[i] == '\\') {
                U32 indice = i;

                for (U32 c = i + 1; p[indice] != '\0'; ++c) {
                    p[indice] = p[c];
                    ++indice;
                }
            }

            lastCharacterWasSeparator = true;
        }
        else {
            lastCharacterWasSeparator = false;
        }

        ++length;
    }

    return length;
}

CHAR*
FilePath::getFileNamePrivate(CHAR temp[], U32* l, bool ie) const{
    for (U32 i = 0; i <= length; ++i)
        temp[i] = path[i];

    PathStripPathA(temp);

    U32 tempLength = 0;
    for (U32 i = 0; temp[i] != '\0'; ++i) {
        if(!ie) {
            if(temp[i] == '.')
                break;
        }
        ++tempLength;
    }

    if(l)
        *l = tempLength;

    return path + (length - tempLength);
}

CHAR*
FilePath::getFileName(U32* l, bool ie) const {
    CHAR temp[length + 1];

    CHAR* fileName = getFileNamePrivate(&temp[0], l, ie);
    return fileName;
}

CHAR*
FilePath::getExtension(U32* l) const {
    CHAR* extension = PathFindExtensionA(path);
    if(l)
        *l = length - ((U64)extension - (U64)path);
    return extension;
}

CHAR
FilePath::getDriveLetter(void) const {
    I32 drive = PathGetDriveNumberA(path);
    CHAR letter = 0;
    if(drive != -1) {
        letter = 65 + drive;
    }
    return letter;
}

I32
FilePath::simplifyPath(void) {
    I32 l;

    CHAR tempPath[260];

    PathCanonicalizeA(tempPath, path);

    U32 tempLength = 0;
    for (U32 i = 0; tempPath[i] != '\0'; ++i)
        ++tempLength;

    l = (tempLength - length);
    length = tempLength;

    MemoryManager::free(path);
    path = (CHAR*)MemoryManager::rawAlloc(length + 1);

    for (U32 i = 0; i <= length; ++i) {
        path[i] = tempPath[i];
    }

    return l;
}

bool
FilePath::pathExists(void) const {
    return PathFileExistsA(path);
}

bool
FilePath::isRelative(void) const {
    return PathIsRelativeA(path);
}

bool
FilePath::isDirectory(void) const {
    return PathIsDirectoryA(path);
}

}

#endif
